#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from ... import Mapping
from .stix2_observable_objects_converter import (
    ExternalSTIX2SampleObservableConverter,
    InternalSTIX2SampleObservableConverter)
from .stix2converter import (
    ExternalSTIX2Converter, InternalSTIX2Converter, STIX2Converter)
from .stix2mapping import STIX2Mapping
from abc import ABCMeta
from typing import TYPE_CHECKING, Union

if TYPE_CHECKING:
    from ..external_stix2_to_misp import ExternalSTIX2toMISPParser
    from ..internal_stix2_to_misp import InternalSTIX2toMISPParser

_MAIN_PARSER_TYPING = Union[
    'ExternalSTIX2toMISPParser', 'InternalSTIX2toMISPParser'
]


class STIX2MalwareAnalysisMapping(STIX2Mapping):
    __malware_analysis_object_mapping = Mapping(
        analysis_definition_version={
            'type': 'text', 'object_relation': 'analysis_definition_version'
        },
        analysis_ended={'type': 'datetime', 'object_relation': 'end_time'},
        analysis_engine_version={
            'type': 'text', 'object_relation': 'analysis_engine_version'
        },
        analysis_started={
            'type': 'datetime', 'object_relation': 'start_time'
        },
        configuration_version={
            'type': 'text', 'object_relation': 'configuration_version'
        },
        modules={'type': 'text', 'object_relation': 'module'},
        product={'type': 'text', 'object_relation': 'product'},
        result={'type': 'text', 'object_relation': 'result'},
        result_name={'type': 'text', 'object_relation': 'result_name'},
        submitted={'type': 'datetime', 'object_relation': 'submitted_time'},
        version={'type': 'text', 'object_relation': 'version'}
    )

    @classmethod
    def malware_analysis_object_mapping(cls) -> dict:
        return cls.__malware_analysis_object_mapping


class STIX2MalwareAnalysisConverter(STIX2Converter, metaclass=ABCMeta):
    def __init__(self, main: _MAIN_PARSER_TYPING):
        self._set_main_parser(main)
        self._mapping = STIX2MalwareAnalysisMapping

    def parse(self, malware_analysis_ref: str):
        malware_analysis = self.main_parser._get_stix_object(
            malware_analysis_ref
        )
        analysis_object = self._create_misp_object(
            'malware-analysis', malware_analysis
        )
        for attribute in self._generic_parser(malware_analysis):
            analysis_object.add_attribute(**attribute)
        misp_object = self.main_parser._add_misp_object(
            analysis_object, malware_analysis
        )
        if hasattr(malware_analysis, 'host_vm_ref'):
            host_vm = self._converter._parse_software_observable_object(
                malware_analysis.host_vm_ref, malware_analysis
            )
            misp_object.add_reference(host_vm.uuid, 'vm-hosted-by')
        if hasattr(malware_analysis, 'operating_system_ref'):
            operatingSystem = self._converter._parse_software_observable_object(
                malware_analysis.operating_system_ref, malware_analysis
            )
            misp_object.add_reference(operatingSystem.uuid, 'hosted-by')
            if hasattr(malware_analysis, 'installed_software_refs'):
                for software_ref in malware_analysis.installed_software_refs:
                    soft = self._converter._parse_software_observable_object(
                        software_ref, malware_analysis
                    )
                    operatingSystem.add_reference(soft.uuid, 'installs')
        if hasattr(malware_analysis, 'analysis_sco_refs'):
            for analysis_ref in malware_analysis.analysis_sco_refs:
                feature = getattr(
                    self._converter,
                    f"_parse_{analysis_ref.split('--')[0]}_observable_object"
                )
                analysis_sco = feature(analysis_ref, malware_analysis)
                misp_object.add_reference(analysis_sco.uuid, 'captured')
        if hasattr(malware_analysis, 'sample_ref'):
            sample_ref = malware_analysis.sample_ref
            feature = getattr(
                self._converter,
                f"_parse_{sample_ref.split('--')[0]}_observable_object"
            )
            sample = feature(sample_ref, malware_analysis)
            misp_object.add_reference(sample.uuid, 'analyses')


class ExternalSTIX2MalwareAnalysisConverter(
        STIX2MalwareAnalysisConverter, ExternalSTIX2Converter):
    def __init__(self, main: 'ExternalSTIX2toMISPParser'):
        super().__init__(main)
        self._converter = ExternalSTIX2SampleObservableConverter(self)


class InternalSTIX2MalwareAnalysisConverter(
        STIX2MalwareAnalysisConverter, InternalSTIX2Converter):
    def __init__(self, main: 'InternalSTIX2toMISPParser'):
        super().__init__(main)
        self._converter = InternalSTIX2SampleObservableConverter(self)
